package cn.chendd.jsch.utils;

import com.jcraft.jsch.*;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;

import java.io.OutputStream;
import java.util.*;

/**
 * sftp操作工具类
 *
 * @author chendd
 * @date 2023/11/18 9:12
 */
@Getter
public final class SftpUtils {

    /**
     * 路径分隔符
     */
    private static final String SEPARATOR = "/";

    private Session session;

    private ChannelSftp channel;

    /**
     * 根据sftp连接参数获取工具类实例
     * @param param 参数对象
     * @return 工具类实例
     */
    public static SftpUtils newInstance(SftpParam param) {
        JSch jsch = new JSch();
        SftpUtils sftp = new SftpUtils();
        try {
            sftp.session = jsch.getSession(param.getUsername(), param.getHost(), param.getPort());
            final String password = param.getPassword();
            if (StringUtils.isNotEmpty(password)) {
                sftp.session.setPassword(password);
            }
            sftp.session.setTimeout(param.getTimeout());
            sftp.session.setConfig("StrictHostKeyChecking" , "no");
            sftp.session.connect();
            sftp.channel = (ChannelSftp) sftp.session.openChannel("sftp");
            sftp.channel.connect();
        } catch (JSchException e) {
            if (sftp.session != null) {
                sftp.session.disconnect();
            }
            throw new RuntimeException(e);
        }
        return sftp;
    }

    /**
     * 上传文件【默认覆盖】
     * @param src 本地源文件路径
     * @param dest 远程目标路径
     */
    public void uploadFile(String src, String dest) throws SftpException {
        this.channel.put(src , dest);
    }

    /**
     * 下载文件
     * @param src 远程源文件
     * @param dest 本地目标文件
     */
    public void downloadFile(String src, String dest) throws SftpException {
        this.channel.get(src , dest);
    }

    /**
     * 根据文件夹路径获取文件列表
     * @param path 路径
     * @return 数据对象
     * @throws SftpException 异常处理
     */
    public List<FileEntry> listFiles(String path) throws SftpException {
        @SuppressWarnings("unchecked")
        final Vector<ChannelSftp.LsEntry> files = (Vector<ChannelSftp.LsEntry>) this.channel.ls(path);
        ChannelSftp.LsEntry item;
        List<String> ignore = Arrays.asList(".", "..");
        List<FileEntry> list = new ArrayList<>();
        for (ChannelSftp.LsEntry entry : files) {
            final String name = entry.getFilename();
            if (ignore.contains(name)) {
                continue;
            }
            final SftpATTRS attr = entry.getAttrs();
            list.add(FileEntry.builder()
                    .name(name).size(attr.getSize()).updateTime(new Date(attr.getMTime() * 1000L))
                    .permission(attr.getPermissionsString()).dir(attr.isDir()).longname(entry.getLongname())
                    .build());
        }
        return list;
    }

    /**
     * 重命名文件（夹）
     * @param oldpath 旧文件（夹）路径
     * @param newpath 新文件（夹）路径
     */
    public void rename(String oldpath, String newpath) throws SftpException {
        this.channel.rename(oldpath , newpath);
    }

    /**
     * 删除文件
     * @param path 文件路径
     */
    public void removeFile(String path) throws SftpException {
        this.channel.rm(path);
    }

    /**
     * 删除文件夹【要求删除的文件夹必须为空文件夹】
     * @param path 文件夹路径
     */
    public void removeFolder(String path) throws SftpException {
        this.channel.rmdir(path);
    }

    /**
     * 判断路径是否为文件夹
     * @param path 文件路径
     * @return true ？文件夹 ：非文件夹
     */
    public boolean isDir(String path) {
        try {
            SftpATTRS attrs = this.channel.lstat(path);
            return attrs.isDir();
        } catch (SftpException e) {
            return false;
        }
    }

    /**
     * 路径是否存在【约定文件夹以‘/’结尾】
     * @param path 路径
     * @return true ? 存在 ：不存在
     */
    public boolean exists(String path) {
        try {
            SftpATTRS attrs = this.channel.lstat(path);
            final boolean dir = attrs.isDir();
            if (StringUtils.endsWith(path , SEPARATOR)) {
                return dir;
            } else {
                return !dir;
            }
        } catch (SftpException e) {
            return false;
        }
    }

    /**
     * 创建文件夹路径
     * @param path 文件夹路径
     * @throws SftpException 异常定义
     */
    public void mkdirs(String path) throws SftpException {
        if (! StringUtils.endsWith(path , SEPARATOR)) {
            path = path + SEPARATOR;
        }
        if (this.isDir(path)) {
            return;
        }
        String[] paths = StringUtils.split(path , SEPARATOR);
        StringBuilder pathBuilder = new StringBuilder(SEPARATOR);
        String temp;
        for (String p : paths) {
            if (StringUtils.isEmpty(p)) {
                continue;
            }
            pathBuilder.append(p).append(SEPARATOR);
            temp = pathBuilder.toString();
            if (this.exists(temp)) {
                continue;
            }
            this.channel.mkdir(temp);
        }
    }

    /**
     * 释放连接
     */
    public void disconnect() {
        if (this.channel != null && this.channel.isConnected()) {
            this.channel.disconnect();
        }

        if (this.session != null && this.session.isConnected()) {
            this.session.disconnect();
        }
    }

    /**
     * 文件对象
     */
    @Data
    @Builder
    public static class FileEntry {

        /**
         * 文件（夹）名称
         */
        private String name;
        /**
         * 文件大小
         */
        private long size;
        /**
         * 更新时间
         */
        private Date updateTime;
        /**
         * 权限
         */
        private String permission;
        /**
         * 是否为文件夹
         */
        private boolean dir;
        /**
         * 文件或目录的详细信息，包括权限、所有者、所属组、大小、修改时间等
         */
        private String longname;

    }

}
